import { useState, useEffect } from 'react'; export default function App() { // Auth0 Configuration - Your actual Auth0 credentials const AUTH0_DOMAIN = 'ahnaflegaldatabase.au.auth0.com'; const AUTH0_CLIENT_ID = 'UXoGRxPU4l2tZdNyECGOMLnJendWpPsn'; const AUTH0_REDIRECT_URI = window.location.origin; // Authentication state const [isAuthenticated, setIsAuthenticated] = useState(false); const [currentUser, setCurrentUser] = useState(null); const [showLogin, setShowLogin] = useState(false); const [isLoading, setIsLoading] = useState(true); const [authError, setAuthError] = useState(''); // Auth0 token and user management const [accessToken, setAccessToken] = useState(null); const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const [activeTab, setActiveTab] = useState('dashboard'); const [showAddForm, setShowAddForm] = useState(false); const [notification, setNotification] = useState({ message: '', type: '' }); const [selectedDoc, setSelectedDoc] = useState(null); const [filters, setFilters] = useState({ type: '', jurisdiction: '', dateFrom: '', dateTo: '' }); const [showFilters, setShowFilters] = useState(false); const [searchHistory, setSearchHistory] = useState([]); // Load documents from localStorage or use default data const loadDocumentsFromStorage = () => { try { const stored = localStorage.getItem('aharenge_documents'); if (stored) { return JSON.parse(stored); } } catch (error) { console.error('Error loading documents from storage:', error); } // Return default sample data if nothing stored or error occurred return [ { id: 1, type: 'case_law', title: 'Smith v. Jones', dhivehi_title: 'ސްމިތް ވީ. ޖޯންސް', jurisdiction: 'California', date: '2022-03-15', citation: '123 Cal. App. 4th 567 (2022)', keywords: ['contract', 'employment', 'breach'], judges: ['Justice Williams', 'Justice Chen', 'Justice Rodriguez'], introduction: { en: 'This case involves a breach of contract dispute between an employer and employee regarding non-compete clauses and severance agreements.', dv: 'މި ކޭސް އަކީ މުވައްޒަފާއި މުވައްޒަފުގެ ދަށުން ކޮންޓްރެކްޓް ބްރީޗް ދެކޮޅަށް ވާ ކޭސެކެވެ.' }, ratio: { en: 'The court held that failure to fulfill contractual obligations constitutes breach when material terms are violated without justification.', dv: 'ކޯޓުން ނިންމީ ކޮންޓްރެކްޓް އޮބްލިގޭޝަންތައް ފުރިހަމަ ނުކުރުމަކީ ބްރީޗް ކަމުގައެވެ.' }, abstracts: [ { en: 'In the matter of Smith v. Jones, the court ruled on the enforceability of non-compete clauses in employment contracts.', dv: 'ސްމިތް ވީ. ޖޯންސް ކޭސްގައި ކޯޓުން ނިންމީ ނޮން-ކޮމްޕީޓް ކްލޯސްތަކުގެ ބާރަށް ޢަމަލުކުރެވޭ ކަމެވެ.', page: 'p. 567', paragraph: '¶ 12' } ] }, { id: 2, type: 'statute', title: 'Consumer Protection Act', dhivehi_title: 'ސަރުކާރުގެ ޙިމާޔަތުގެ ޤާނޫނު', jurisdiction: 'Federal', date: '2023-01-20', citation: '15 U.S.C. § 1681', keywords: ['consumer', 'protection', 'fraud', 'warranty'], body: { en: 'This statute provides comprehensive consumer protection measures against fraudulent business practices and defective products. It establishes clear guidelines for warranty claims, refund policies, and dispute resolution mechanisms.', dv: 'މި ޤާނޫނުން ފުރިހަމަ ކަސްޓަމަރުގެ ޙިމާޔަތް ފޯރުކޮށްދޭ ފުރޯޑް އަދި ކްރޮޅު ތަކެތީގެ ކުރިއެރުން. މި ޤާނޫނުން ވޮރަންޓީ ކްލެއިމް، ރިފަންޑް ޕޮލިސީ އަދި ޑިސްޕިއުޓް ރިޒޮލިއުޝަން މެކެނިޒަމަށް ސާފު ގައިޑްލައިން ބަޔާންކޮށްދެއެވެ.' } }, { id: 3, type: 'legal_article', title: 'Recent Developments in Contract Law', dhivehi_title: 'ކޮންޓްރެކްޓް ލޯގެ އެއްވެރި ކުރިމަތިއްޔާއިގެ އެއްވެރި ކުރިމަތިއްޔާ', jurisdiction: 'International', date: '2024-05-15', citation: 'Int\'l L.J. 2024 Vol. 1', keywords: ['contract', 'law', 'development', 'analysis'], body: { en: 'This article analyzes recent developments in contract law across multiple jurisdictions, focusing on digital contracts, force majeure clauses, and evolving standards of good faith. The analysis covers landmark cases, statutory reforms, and emerging legal principles that shape modern contractual relationships.', dv: 'މި އަރިކަލް ކޮންޓްރެކްޓް ލޯގެ އެއްވެރި ކުރިމަތިއްޔާ ބާރުކުރެވޭ އެވެ، ޑިޖިޓަލް ކޮންޓްރެކްޓް، ފޮސް މާޖުރް ކްލޯސް، އަދި ގޫމް ފޭތް ސްޓެންޑާރޑްތަކުގެ އެއްވެރި ކުރިމަތިއްޔާ ރިވިއު ކުރެއެވެ. މި ބާރުކުރުމުގައި ލޭންޑްމާކް ކޭސްތައް، ޤާނޫނީ ރިފޯމް، އަދި މޮޑަން ކޮންޓްރެކްޗުއަލް ރިލޭޝަންޝިޕްތައް ޝޭޕްކުރާ އިމަރޖިންގ ލީގަލް ޕްރިންސިޕަލްތައް ހިމެނެއެވެ.' } }, { id: 4, type: 'case_law', title: 'Johnson v. Pacific Corp', dhivehi_title: 'ޖޮންސަން ވީ. ޕެސިފިކް ކޯޕް', jurisdiction: 'New York', date: '2023-11-08', citation: '2023 NY Slip Op 05234', keywords: ['corporate', 'liability', 'negligence', 'damages'], judges: ['Judge Thompson', 'Judge Martinez'], introduction: { en: 'A landmark case establishing corporate liability standards for negligent supervision in workplace accidents.', dv: 'މަސައްކަތުގެ ތަނުގެ ހާދިސާތަކުގައި ނެގްލިޖަންޓް ސުޕަވިޝަންއަށް ކޯޕޮރޭޓް ލަޔަބިލިޓީ ސްޓެންޑަރޑް ޤާއިމްކުރި މުހިންމު ކޭސެކެވެ.' }, ratio: { en: 'Corporations have a non-delegable duty to ensure adequate supervision of safety protocols.', dv: 'ކޯޕޮރޭޝަންތަކުން ސޭފްޓީ ޕްރޮޓޮކޯލްތަކުގެ ފުރިހަމަ ސުޕަވިޝަން ކަށަވަރު ކުރުމުގެ މަސްއޫލިއްޔަތެއް އެވެ.' }, abstracts: [ { en: 'The court found that Pacific Corp failed to implement adequate safety measures despite prior warnings.', dv: 'ކޯޓުން ފެނުނީ ޕެސިފިކް ކޯޕްއަށް ކުރިން ވާރުނިންގް ދީފިނަމަވެސް ފުރިހަމަ ސޭފްޓީ މިންހާސް ޢަމަލުކުރެއްނުވި ކަމެވެ.', page: 'p. 234', paragraph: '¶ 45' } ] }, { id: 5, type: 'statute', title: 'Data Privacy Protection Act', dhivehi_title: 'ޑޭޓާ ޕްރައިވަސީ ޙިމާޔަތުގެ ޤާނޫނު', jurisdiction: 'California', date: '2024-01-01', citation: 'Cal. Civ. Code § 1798.100', keywords: ['privacy', 'data', 'protection', 'rights'], body: { en: 'This act establishes comprehensive data privacy rights for California residents, including the right to know, delete, and opt-out of the sale of personal information. Companies must provide clear disclosures about data collection practices and implement reasonable security measures.', dv: 'މި ޤާނޫނުން ކެލިފޯނިޔާ ރެސިޑެންޓްތަކަށް ފުރިހަމަ ޑޭޓާ ޕްރައިވަސީ ޙައްޤުތައް ޤާއިމްކުރޭ، ނޭޖަހާ، ޑިލީޓް، އަދި ޕާސަނަލް އިންފޮމޭޝަން ވިއްކުމުން އާއްޗް އައުޓް ވުމުގެ ޙައްޤު ހިމެނޭ. ކުންފުނިތަކުން ޑޭޓާ ސަގުކުރުމުގެ ޕްރެކްޓިސްތަކާ މެދު ސާފު ޑިސްކްލޯޝަރ ދޭންވާނެ އަދި އަކުރަ ސެކިއުރިޓީ މިންހާސް ތައް ޢަމަލުކުރަންވާނެއެވެ.' } }, { id: 6, type: 'legal_article', title: 'AI and Legal Ethics: Emerging Challenges', dhivehi_title: 'އޭއައި އަދި ލީގަލް އެތިކްސް: އުފެދޭ ގޮންޖެހުން', jurisdiction: 'International', date: '2024-06-20', citation: 'Tech Law Review 2024 Vol. 15', keywords: ['artificial intelligence', 'ethics', 'legal profession', 'technology'], body: { en: 'This comprehensive analysis explores the ethical implications of AI adoption in legal practice, addressing concerns about bias, accountability, and professional responsibility. The article examines case studies, regulatory frameworks, and best practices for ethical AI implementation in law firms.', dv: 'މި ފުރިހަމަ ތަޙްލީލުން ލީގަލް ޕްރެކްޓިސްގައި އޭއައި ބޭނުންކުރުމުގެ އެތިކަލް އަސަރުތައް ބަލާ، ބަޔާސް، އެކައުންޓެބިލިޓީ، އަދި ޕްރޮފެޝަނަލް ރެސްޕޮންސިބިލިޓީގެ ކަންބޮޑުވުންތައް ކުރިއަށް ގެންދޭ. މި އަރޓިކަލްގައި ކޭސް ސްޓަޑީ، ރެގިއުލޭޓަރީ ފްރޭމްވާކް، އަދި ލޯ ފާމްތަކުގައި އެތިކަލް އޭއައި އިމްޕްލިމަންޓޭޝަނަށް ރަނގަޅު ގޮތްތައް ބައްލަވާނެއެވެ.' } } ]; }; const [legalDocuments, setLegalDocuments] = useState(() => loadDocumentsFromStorage()); // Save documents to localStorage whenever legalDocuments changes useEffect(() => { try { localStorage.setItem('aharenge_documents', JSON.stringify(legalDocuments)); console.log('Documents saved to localStorage'); } catch (error) { console.error('Error saving documents to storage:', error); showNotification('Warning: Documents could not be saved locally', 'warning'); } }, [legalDocuments]); // Initialize results with all documents on first load useEffect(() => { if (isAuthenticated) { setResults(legalDocuments); } }, [isAuthenticated, legalDocuments]); // Calculate next ID safely const nextId = legalDocuments.reduce((max, doc) => Math.max(max, doc.id), 0) + 1; // Enhanced new document state const [newDoc, setNewDoc] = useState({ type: 'case_law', title: '', dhivehi_title: '', jurisdiction: '', date: '', citation: '', keywords: '', judges: '', introduction: { en: '', dv: '' }, ratio: { en: '', dv: '' }, body: { en: '', dv: '' }, abstracts: [{ en: '', dv: '', page: '', paragraph: '' }] }); // Auth0 Integration Functions // Initialize Auth0 on component mount useEffect(() => { initializeAuth0(); }, []); const initializeAuth0 = async () => { try { setIsLoading(true); // Check for existing token in localStorage const storedToken = localStorage.getItem('auth0_access_token'); const storedUser = localStorage.getItem('auth0_user'); if (storedToken && storedUser) { // Verify token is still valid (simplified check) try { const userInfo = JSON.parse(storedUser); setAccessToken(storedToken); setCurrentUser({ id: userInfo.sub, name: userInfo.name || userInfo.email, email: userInfo.email, role: userInfo['https://aharenge.com/roles']?.[0] || 'user', // Custom claim for role picture: userInfo.picture }); setIsAuthenticated(true); showNotification(`Welcome back, ${userInfo.name || userInfo.email}!`, 'success'); } catch (e) { // Clear invalid stored data localStorage.removeItem('auth0_access_token'); localStorage.removeItem('auth0_user'); } } // Check for Auth0 callback (code in URL) const urlParams = new URLSearchParams(window.location.search); const authCode = urlParams.get('code'); const state = urlParams.get('state'); const storedState = localStorage.getItem('auth0_state'); if (authCode) { // Verify the state parameter to prevent CSRF attacks if (state && state === storedState) { await handleAuthCallback(authCode); } else { console.error('State mismatch - possible CSRF attack'); setAuthError('Authentication failed - security validation error'); showNotification('Authentication failed - security error', 'error'); } } } catch (error) { console.error('Auth0 initialization error:', error); setAuthError('Failed to initialize authentication'); } finally { setIsLoading(false); } }; const handleAuthCallback = async (code) => { try { showNotification('Processing login...', 'info'); // Exchange code for tokens using Auth0 token endpoint const verifier = localStorage.getItem('auth0_code_verifier'); if (!verifier) { throw new Error('Code verifier not found'); } const tokenResponse = await fetch(`https://${AUTH0_DOMAIN}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ grant_type: 'authorization_code', client_id: AUTH0_CLIENT_ID, code_verifier: verifier, code: code, redirect_uri: AUTH0_REDIRECT_URI }) }).catch(error => { console.error('Network error connecting to Auth0:', error); throw new Error('Cannot connect to Auth0 server. Please check your Auth0 domain configuration.'); }); if (!tokenResponse.ok) { throw new Error('Failed to exchange code for tokens'); } const tokens = await tokenResponse.json(); // Get user information using the access token const userInfoResponse = await fetch(`https://${AUTH0_DOMAIN}/userinfo`, { headers: { 'Authorization': `Bearer ${tokens.access_token}` } }); if (!userInfoResponse.ok) { throw new Error('Failed to fetch user information'); } const userInfo = await userInfoResponse.json(); // Save tokens and user info to localStorage localStorage.setItem('auth0_access_token', tokens.access_token); localStorage.setItem('auth0_id_token', tokens.id_token); localStorage.setItem('auth0_user', JSON.stringify(userInfo)); // Set user in application state setAccessToken(tokens.access_token); setCurrentUser({ id: userInfo.sub, name: userInfo.name || userInfo.email, email: userInfo.email, role: userInfo['https://aharenge.com/roles']?.[0] || 'user', // Custom claim for role picture: userInfo.picture }); setIsAuthenticated(true); // Clear the URL parameters window.history.replaceState({}, document.title, window.location.pathname); showNotification(`Welcome, ${userInfo.name || userInfo.email}!`, 'success'); } catch (error) { console.error('Auth callback error:', error); setAuthError('Failed to complete login'); showNotification('Authentication failed. Please try again.', 'error'); } }; // Generate a code verifier for PKCE (Proof Key for Code Exchange) const generateCodeVerifier = () => { const array = new Uint8Array(32); window.crypto.getRandomValues(array); return btoa(String.fromCharCode.apply(null, array)) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); }; // Generate a code challenge from the code verifier const generateCodeChallenge = async (codeVerifier) => { const encoder = new TextEncoder(); const data = encoder.encode(codeVerifier); const digest = await window.crypto.subtle.digest('SHA-256', data); return btoa(String.fromCharCode.apply(null, new Uint8Array(digest))) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); }; const loginWithAuth0 = async () => { try { // First, test if we can reach the Auth0 domain const testUrl = `https://${AUTH0_DOMAIN}/.well-known/openid_configuration`; try { const testResponse = await fetch(testUrl, { method: 'HEAD' }); if (!testResponse.ok) { throw new Error('Auth0 domain not reachable'); } } catch (testError) { console.error('Auth0 domain test failed:', testError); showNotification('Cannot connect to Auth0. Please check your domain configuration or use Demo Login instead.', 'error'); return; } // Generate PKCE code verifier and challenge const codeVerifier = generateCodeVerifier(); const codeChallenge = await generateCodeChallenge(codeVerifier); // Store code verifier in localStorage for later use localStorage.setItem('auth0_code_verifier', codeVerifier); // Generate state parameter to prevent CSRF attacks const state = generateRandomState(); localStorage.setItem('auth0_state', state); // Build Auth0 authorization URL with PKCE const authUrl = `https://${AUTH0_DOMAIN}/authorize?` + `response_type=code&` + `client_id=${AUTH0_CLIENT_ID}&` + `redirect_uri=${encodeURIComponent(AUTH0_REDIRECT_URI)}&` + `scope=openid profile email&` + `state=${state}&` + `code_challenge=${codeChallenge}&` + `code_challenge_method=S256`; // Redirect to Auth0 login window.location.href = authUrl; } catch (error) { console.error('Error initiating Auth0 login:', error); showNotification('Auth0 connection failed. Please use Demo Login or check your Auth0 configuration.', 'error'); } }; const generateRandomState = () => { return Math.random().toString(36).substring(2) + Date.now().toString(36); }; const handleLogout = () => { // Clear all Auth0-related items from local storage localStorage.removeItem('auth0_access_token'); localStorage.removeItem('auth0_id_token'); localStorage.removeItem('auth0_user'); localStorage.removeItem('auth0_state'); localStorage.removeItem('auth0_code_verifier'); // Reset application state setIsAuthenticated(false); setCurrentUser(null); setAccessToken(null); setActiveTab('dashboard'); setResults([]); // Build Auth0 logout URL const logoutUrl = `https://${AUTH0_DOMAIN}/v2/logout?` + `client_id=${AUTH0_CLIENT_ID}&` + `returnTo=${encodeURIComponent(window.location.origin)}`; showNotification('Logged out successfully', 'info'); // Redirect to Auth0 logout endpoint window.location.href = logoutUrl; }; // Alternative: Simple demo login (fallback if Auth0 not configured) const demoLogin = async () => { try { setIsLoading(true); // Simulate API call delay await new Promise(resolve => setTimeout(resolve, 1000)); // Create demo user const demoUser = { id: 'demo_' + Date.now(), name: 'Demo User', email: 'demo@aharenge.com', role: 'admin', // Give demo user admin privileges picture: null }; setCurrentUser(demoUser); setIsAuthenticated(true); setShowLogin(false); setActiveTab('dashboard'); // Store demo session localStorage.setItem('demo_user', JSON.stringify(demoUser)); showNotification(`Welcome, ${demoUser.name}!`, 'success'); } catch (error) { setAuthError('Demo login failed'); } finally { setIsLoading(false); } }; // Custom SVG Icons const SearchIcon = () => ( ); const BookOpenIcon = () => ( ); const DashboardIcon = () => ( ); const PlusIcon = () => ( ); const XIcon = () => ( ); const FileTextIcon = () => ( ); const CalendarIcon = () => ( ); const MapPinIcon = () => ( ); const TagIcon = () => ( ); const EyeIcon = () => ( ); const TrashIcon = () => ( ); const FilterIcon = () => ( ); const DownloadIcon = () => ( ); const DocumentIcon = () => ( ); const UserIcon = () => ( ); const LogoutIcon = () => ( ); const LoginIcon = () => ( ); // Auto-hide notifications useEffect(() => { if (notification.message) { const timer = setTimeout(() => { setNotification({ message: '', type: '' }); }, 5000); return () => clearTimeout(timer); } }, [notification]); const showNotification = (message, type = 'success') => { setNotification({ message, type }); }; // CSV Export function const exportToCSV = () => { const headers = ['ID', 'Type', 'Title', 'Jurisdiction', 'Date', 'Citation', 'Keywords', 'Judges']; const csvData = legalDocuments.map(doc => [ doc.id, doc.type, doc.title || doc.dhivehi_title, doc.jurisdiction, doc.date, doc.citation || 'N/A', doc.keywords.join('; '), doc.judges ? doc.judges.join('; ') : 'N/A' ]); const csvContent = [headers, ...csvData] .map(row => row.map(field => `"${field}"`).join(',')) .join('\n'); const blob = new Blob([csvContent], { type: 'text/csv' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'aharenge_documents.csv'; link.click(); window.URL.revokeObjectURL(url); showNotification('CSV export completed', 'success'); }; // PDF Export function - simplified approach const exportToPDF = () => { try { // Simple text-based PDF export that works reliably const pdfContent = legalDocuments.map((doc, index) => { let content = `${index + 1}. ${doc.title || doc.dhivehi_title}\n`; content += `Jurisdiction: ${doc.jurisdiction}\n`; content += `Date: ${doc.date}\n`; if (doc.citation) content += `Citation: ${doc.citation}\n`; if (doc.keywords?.length) content += `Keywords: ${doc.keywords.join(', ')}\n`; if (doc.judges?.length) content += `Judges: ${doc.judges.join(', ')}\n`; if (doc.type === 'case_law') { if (doc.introduction?.en) content += `\nIntroduction:\n${doc.introduction.en}\n`; if (doc.ratio?.en) content += `\nRatio Decidendi:\n${doc.ratio.en}\n`; } else if (doc.body?.en) { content += `\nContent:\n${doc.body.en}\n`; } return content + '\n' + '='.repeat(80) + '\n\n'; }).join(''); const fullContent = `AHARENGE DATABASE - DOCUMENT EXPORT Generated: ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()} ${'='.repeat(80)} ${pdfContent}`; // Create and download as PDF-like text file const blob = new Blob([fullContent], { type: 'application/pdf' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `aharenge_documents_${new Date().toISOString().split('T')[0]}.pdf`; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); showNotification('Document export completed successfully', 'success'); } catch (error) { console.error('Export error:', error); showNotification('Export failed. Please try again.', 'error'); } }; const parseQuery = (input) => { // Enhanced parsing to handle both English and Dhivehi text const tokens = input.match(/"[^"]+"|\S+/g) || []; const parsed = { keywords: [], jurisdictions: [] }; const jurisdictionRegex = /\b(california|new york|ny|federal|maldives|mv|ކެލިފޯނިޔާ|ނިއު ޔޯކް|ފެޑެރަލް|ދިވެހިރާއްޖެ)\b/gi; let match; while ((match = jurisdictionRegex.exec(input)) !== null) { parsed.jurisdictions.push(match[0].toLowerCase()); } tokens.forEach(token => { if (token.startsWith('"') && token.endsWith('"')) { // Quoted phrase - keep as is (remove quotes) parsed.keywords.push(token.slice(1, -1)); } else { // Individual words - split on whitespace but preserve Dhivehi characters const words = token.split(/\s+/).filter(word => word.trim().length > 0); parsed.keywords.push(...words); } }); // Remove empty keywords and duplicates parsed.keywords = [...new Set(parsed.keywords.filter(keyword => keyword.trim().length > 0))]; return parsed; }; // Database Backup and Restore Functions const exportDatabase = () => { try { const databaseExport = { version: '1.0', exportDate: new Date().toISOString(), documentCount: legalDocuments.length, documents: legalDocuments }; const dataStr = JSON.stringify(databaseExport, null, 2); const blob = new Blob([dataStr], { type: 'application/json' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `aharenge_database_backup_${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); showNotification(`Database backup completed! ${legalDocuments.length} documents exported.`, 'success'); } catch (error) { console.error('Export error:', error); showNotification('Failed to export database. Please try again.', 'error'); } }; const importDatabase = (event) => { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { try { const importedData = JSON.parse(e.target.result); // Validate the imported data if (!importedData.documents || !Array.isArray(importedData.documents)) { throw new Error('Invalid file format'); } // Ask user for confirmation const confirmMessage = `This will replace your current database with ${importedData.documents.length} documents from the backup file. Your current ${legalDocuments.length} documents will be lost. Are you sure?`; if (window.confirm(confirmMessage)) { setLegalDocuments(importedData.documents); setResults(importedData.documents); showNotification(`Database restored successfully! ${importedData.documents.length} documents imported.`, 'success'); } } catch (error) { console.error('Import error:', error); showNotification('Failed to import database. Please check the file format.', 'error'); } }; reader.readAsText(file); // Reset the input so the same file can be selected again event.target.value = ''; }; // Enhanced full-text search function const searchInText = (text, searchTerms) => { if (!text || !searchTerms.length) return false; const normalizedText = text.toLowerCase().trim(); return searchTerms.some(term => normalizedText.includes(term.toLowerCase())); }; const search = () => { if (!query.trim()) { showNotification('Please enter a search query', 'error'); return; } const { keywords, jurisdictions } = parseQuery(query); const matchedDocuments = []; // Enhanced search that looks through all text content legalDocuments.forEach(doc => { let hasMatch = false; // Search in basic fields if (searchInText(doc.title, keywords) || searchInText(doc.dhivehi_title, keywords) || searchInText(doc.citation, keywords) || searchInText(doc.jurisdiction, keywords)) { hasMatch = true; } // Search in keywords array if (!hasMatch && doc.keywords.some(keyword => keywords.some(term => keyword.toLowerCase().includes(term.toLowerCase())))) { hasMatch = true; } // Search in judges array (for case law) if (!hasMatch && doc.judges && doc.judges.some(judge => keywords.some(term => judge.toLowerCase().includes(term.toLowerCase())))) { hasMatch = true; } // Search in case law specific fields if (!hasMatch && doc.type === 'case_law') { // Search in introduction if (doc.introduction) { if (searchInText(doc.introduction.en, keywords) || searchInText(doc.introduction.dv, keywords)) { hasMatch = true; } } // Search in ratio decidendi if (!hasMatch && doc.ratio) { if (searchInText(doc.ratio.en, keywords) || searchInText(doc.ratio.dv, keywords)) { hasMatch = true; } } // Search in abstracts if (!hasMatch && doc.abstracts) { for (const abstract of doc.abstracts) { if (searchInText(abstract.en, keywords) || searchInText(abstract.dv, keywords) || searchInText(abstract.page, keywords) || searchInText(abstract.paragraph, keywords)) { hasMatch = true; break; } } } } // Search in body content (for statutes and legal articles) if (!hasMatch && (doc.type === 'statute' || doc.type === 'legal_article') && doc.body) { if (searchInText(doc.body.en, keywords) || searchInText(doc.body.dv, keywords)) { hasMatch = true; } } if (hasMatch) { matchedDocuments.push(doc); } }); let filteredResults = matchedDocuments; // Apply filters if (filters.type) { filteredResults = filteredResults.filter(doc => doc.type === filters.type); } if (filters.jurisdiction) { filteredResults = filteredResults.filter(doc => doc.jurisdiction.toLowerCase().includes(filters.jurisdiction.toLowerCase()) ); } if (filters.dateFrom) { filteredResults = filteredResults.filter(doc => new Date(doc.date) >= new Date(filters.dateFrom)); } if (filters.dateTo) { filteredResults = filteredResults.filter(doc => new Date(doc.date) <= new Date(filters.dateTo)); } if (jurisdictions.length > 0) { filteredResults = filteredResults.filter(doc => jurisdictions.includes(doc.jurisdiction.toLowerCase()) ); } setResults(filteredResults); setActiveTab('search'); // Add to search history if (query.trim() && !searchHistory.includes(query.trim())) { setSearchHistory(prev => [query.trim(), ...prev.slice(0, 4)]); } showNotification(`Found ${filteredResults.length} results`, 'info'); }; const handleAddDocument = () => { const { title, dhivehi_title, jurisdiction, date, citation, keywords, judges, introduction, ratio, body, abstracts } = newDoc; // Validation if (!jurisdiction.trim() || !date.trim() || !keywords.trim()) { showNotification("Please fill out all mandatory fields: Jurisdiction, Date, Keywords", 'error'); return; } if (!(title.trim() || dhivehi_title.trim())) { showNotification("Either English or Dhivehi title is required.", 'error'); return; } // Type-specific validation if (newDoc.type === 'case_law') { if (!(introduction.en.trim() || introduction.dv.trim())) { showNotification("Either English or Dhivehi Introduction is required.", 'error'); return; } if (!(ratio.en.trim() || ratio.dv.trim())) { showNotification("Either English or Dhivehi Ratio is required for case law documents.", 'error'); return; } const validAbstracts = abstracts.filter(ab => ab.en.trim() || ab.dv.trim()); if (validAbstracts.length === 0) { showNotification("At least one abstract (English or Dhivehi) is required.", 'error'); return; } for (let i = 0; i < validAbstracts.length; i++) { const ab = validAbstracts[i]; if (!ab.page.trim() || !ab.paragraph.trim()) { showNotification(`Abstract #${i + 1} requires both Page and Paragraph numbers.`, 'error'); return; } } } else if (newDoc.type === 'statute' || newDoc.type === 'legal_article') { if (!(body.en.trim() || body.dv.trim())) { showNotification("Either English or Dhivehi Body is required for this document type.", 'error'); return; } } const keywordList = keywords.split(',').map(k => k.trim().toLowerCase()).filter(Boolean); const judgesList = newDoc.type === 'case_law' && judges ? judges.split(',').map(j => j.trim()).filter(Boolean) : undefined; const newDocument = { id: nextId, type: newDoc.type, title: title.trim(), dhivehi_title: dhivehi_title.trim(), jurisdiction: jurisdiction.trim(), date: date.trim(), citation: newDoc.type === 'case_law' ? citation.trim() : undefined, keywords: keywordList, judges: judgesList, introduction: newDoc.type === 'case_law' ? { en: introduction.en.trim(), dv: introduction.dv.trim() } : undefined, ratio: newDoc.type === 'case_law' ? { en: ratio.en.trim(), dv: ratio.dv.trim() } : undefined, body: (newDoc.type === 'statute' || newDoc.type === 'legal_article') ? { en: body.en.trim(), dv: body.dv.trim() } : undefined, abstracts: newDoc.type === 'case_law' ? abstracts.filter(ab => ab.en.trim() || ab.dv.trim()).map(ab => ({ en: ab.en.trim(), dv: ab.dv.trim(), page: ab.page.trim(), paragraph: ab.paragraph.trim() })) : undefined }; setLegalDocuments(prev => [...prev, newDocument]); setNewDoc({ type: 'case_law', title: '', dhivehi_title: '', jurisdiction: '', date: '', citation: '', keywords: '', judges: '', introduction: { en: '', dv: '' }, ratio: { en: '', dv: '' }, body: { en: '', dv: '' }, abstracts: [{ en: '', dv: '', page: '', paragraph: '' }] }); showNotification("Document added successfully!", 'success'); setShowAddForm(false); }; const removeDocument = (id) => { setLegalDocuments(prev => prev.filter(doc => doc.id !== id)); showNotification("Document removed successfully.", 'success'); }; const exportData = () => { const dataStr = JSON.stringify(legalDocuments, null, 2); const dataUri = 'application/json;charset=utf-8,'+ encodeURIComponent(dataStr); const exportFileDefaultName = 'aharenge_documents.json'; const linkElement = document.createElement('a'); linkElement.setAttribute('href', dataUri); linkElement.setAttribute('download', exportFileDefaultName); linkElement.click(); }; const clearFilters = () => { setFilters({ type: '', jurisdiction: '', dateFrom: '', dateTo: '' }); showNotification('Filters cleared', 'info'); }; const addAbstractField = () => { setNewDoc({ ...newDoc, abstracts: [...newDoc.abstracts, { en: '', dv: '', page: '', paragraph: '' }] }); }; const updateAbstractField = (index, field, value) => { const updatedAbstracts = [...newDoc.abstracts]; updatedAbstracts[index][field] = value; setNewDoc({ ...newDoc, abstracts: updatedAbstracts }); }; const updateIntroField = (lang, value) => { setNewDoc({ ...newDoc, introduction: { ...newDoc.introduction, [lang]: value } }); }; const updateRatioField = (lang, value) => { setNewDoc({ ...newDoc, ratio: { ...newDoc.ratio, [lang]: value } }); }; const updateBodyField = (lang, value) => { setNewDoc({ ...newDoc, body: { ...newDoc.body, [lang]: value } }); }; // Dashboard statistics const getStatistics = () => { const totalDocs = legalDocuments.length; const caseLawCount = legalDocuments.filter(doc => doc.type === 'case_law').length; const statuteCount = legalDocuments.filter(doc => doc.type === 'statute').length; const articleCount = legalDocuments.filter(doc => doc.type === 'legal_article').length; const recentDocs = legalDocuments .sort((a, b) => new Date(b.date) - new Date(a.date)) .slice(0, 3); const jurisdictions = [...new Set(legalDocuments.map(doc => doc.jurisdiction))]; return { totalDocs, caseLawCount, statuteCount, articleCount, recentDocs, jurisdictions: jurisdictions.length }; }; const quickSearchSuggestions = [ { query: 'contract', description: 'Find contract-related cases' }, { query: 'California', description: 'Search California jurisdiction' }, { query: 'employment breach', description: 'Employment law cases' }, { query: 'privacy data', description: 'Data privacy regulations' } ]; // Loading screen if (isLoading) { return (

Loading Aharenge Database...

); } // Public View Component const renderPublicView = () => { return (
{/* Public Header */}

Aharenge Database

Professional Legal Database

{/* Auth Error Display */} {authError && (

Auth0 Connection Issue

{authError}

Try using "Demo Login" to access the application while troubleshooting Auth0.

)} {/* Public Content */}

Professional Legal Database

Comprehensive legal database with bilingual support for legal professionals. Organize case law, statutes, and legal articles with advanced search capabilities.

{/* Auth0 Configuration Notice */}

Authentication Setup

Auth0 Integration Status:

⚠️ Current Issue: Auth0 domain "ahnaflegaldatabase.au.auth0.com" is not responding

Troubleshooting Steps:

  1. Verify your Auth0 domain is correct in your Auth0 dashboard
  2. Check if your Auth0 application is properly configured
  3. Ensure your Auth0 account is active and the domain exists
  4. Add this URL to your Auth0 application's callback URLs:
    {window.location.origin}

Demo Mode: Use the "Demo Login" button for immediate access with admin privileges while troubleshooting Auth0.

{/* Features Grid */}

Advanced Search

Powerful search functionality with filters, keywords, and boolean operators for precise document discovery.

Bilingual Support

Full English and Dhivehi language support for comprehensive legal documentation and accessibility.

Document Organization

Efficiently organize case law, statutes, and legal articles with detailed metadata and categorization.

{/* Call to Action */}

Ready to Get Started?

Access your legal database dashboard and start organizing your case files today.

{/* Public Footer */}
); }; const renderDashboard = () => { const stats = getStatistics(); return (
{/* Welcome Section */}

Welcome to Aharenge Database

Your comprehensive legal database solution with bilingual support

{/* Local Storage Status */}

Your documents are automatically saved locally

All documents persist between browser sessions. Use "Backup Database" to save to your computer.

{/* Statistics Cards */}

Total Documents

{stats.totalDocs}

Case Law

{stats.caseLawCount}

Statutes

{stats.statuteCount}

Jurisdictions

{stats.jurisdictions}

{/* Quick Search & Recent Documents */}
{/* Quick Search */}

Quick Search

{quickSearchSuggestions.map((suggestion, index) => (
{ setQuery(suggestion.query); search(); }} className="p-4 bg-gray-50 rounded-lg cursor-pointer hover:bg-gray-100 transition-colors" >

"{suggestion.query}"

{suggestion.description}

))}
{/* Recent Documents */}

Recent Documents

{stats.recentDocs.map((doc) => (
setSelectedDoc(doc)} className="p-4 bg-gray-50 rounded-lg cursor-pointer hover:bg-gray-100 transition-colors" >

{doc.title || doc.dhivehi_title}

{doc.jurisdiction} {new Date(doc.date).toLocaleDateString()}
{doc.type === 'case_law' ? 'CASE' : doc.type === 'statute' ? 'STATUTE' : 'ARTICLE'}
))}
{/* System Features */}

System Features

Advanced Search

Powerful search with filters, keywords, and boolean operators

Bilingual Support

Full English and Dhivehi language support for all documents

Database Management

Organize case law, statutes, and legal articles efficiently

); }; const renderContent = () => { if (activeTab === 'dashboard') { return renderDashboard(); } else if (activeTab === 'search') { return (
{/* Search Bar */}
setQuery(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && search()} placeholder='Try: "contract AND California" or "employment breach"' className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-gray-800 focus:border-transparent" />
{/* Search History */} {searchHistory.length > 0 && (

Recent searches:

{searchHistory.map((term, index) => ( ))}
)} {/* Advanced Filters */} {showFilters && (

Advanced Filters

setFilters({...filters, jurisdiction: e.target.value})} placeholder="e.g., California" className="w-full p-2 border border-gray-300 rounded-lg" />
setFilters({...filters, dateFrom: e.target.value})} className="w-full p-2 border border-gray-300 rounded-lg" />
setFilters({...filters, dateTo: e.target.value})} className="w-full p-2 border border-gray-300 rounded-lg" />
)}
{/* Search Results */}

Search Results

{results.length > 0 && (

{results.length} documents found

)}
{renderResultsTable(results)}
); } else if (activeTab === 'library') { return (

Document Library

{legalDocuments.length} documents total

{currentUser?.role === 'admin' && ( <> )} {/* Database Backup/Restore Section */}
{renderResultsTable(legalDocuments, currentUser?.role === 'admin')}
); } }; const renderResultsTable = (docs, showActions = false) => { if (docs.length === 0) { return (

No documents found

Try adjusting your search terms or filters

); } return (
{docs.map((doc) => ( ))}
Title Type Jurisdiction Date Keywords Actions
{doc.title || doc.dhivehi_title}
{doc.citation && (
{doc.citation}
)}
{doc.type === 'case_law' ? 'CASE LAW' : doc.type === 'statute' ? 'STATUTE' : 'LEGAL ARTICLE'}
{doc.jurisdiction}
{new Date(doc.date).toLocaleDateString()}
{doc.keywords.slice(0, 3).map((keyword, idx) => ( {keyword} ))} {doc.keywords.length > 3 && ( +{doc.keywords.length - 3} more )}
{showActions && ( )}
); }; // If not authenticated, show public view if (!isAuthenticated) { return renderPublicView(); } return (
{/* Load Tailwind and Faruma font */} {/* Authenticated Header */}

Aharenge Database

Professional Legal Database

{/* User Menu */}

{currentUser?.name}

{currentUser?.role}

{currentUser?.picture && ( Profile )}
{/* Notification */} {notification.message && (
{notification.message}
)} {/* Main Content */}
{renderContent()}
{/* Add Document Form */} {showAddForm && (

Add New Database Entry

{/* Document Type */}
{/* Title Fields */}

Basic Information

setNewDoc({ ...newDoc, title: e.target.value })} className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent" />
setNewDoc({ ...newDoc, dhivehi_title: e.target.value })} className="w-full p-3 border border-gray-300 rounded-lg font-faruma focus:ring-2 focus:ring-gray-800 focus:border-transparent" />
setNewDoc({ ...newDoc, jurisdiction: e.target.value })} className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent" />
setNewDoc({ ...newDoc, date: e.target.value })} className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent" />
{/* Type-Specific Fields */} {newDoc.type === 'case_law' && ( <> {/* Citation for Case Law */}
setNewDoc({ ...newDoc, citation: e.target.value })} className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent" />
{/* Judges Name */}
setNewDoc({ ...newDoc, judges: e.target.value })} placeholder="Enter judges separated by commas (e.g., Justice Smith, Justice Johnson)" className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent" />

Enter multiple judges separated by commas

{/* Introduction */}

Introduction

{/* Ratio Decidendi */}

Ratio Decidendi

{/* Abstracts */}

Abstracts

{newDoc.abstracts.map((ab, index) => (
Abstract #{index + 1} {newDoc.abstracts.length > 1 && ( )}
updateAbstractField(index, 'page', e.target.value)} placeholder="e.g., p. 123" className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent" />
updateAbstractField(index, 'paragraph', e.target.value)} placeholder="e.g., ¶ 12" className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent" />
))}
)} {(newDoc.type === 'statute') && (

Statute Details

)} {(newDoc.type === 'legal_article') && (

Article Content

)} {/* Common Fields */}

Common Fields

setNewDoc({ ...newDoc, keywords: e.target.value })} placeholder="e.g., contract, breach, employment" className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-gray-800 focus:border-transparent" />
{/* Submit Button */}
)} {/* Document Detail Modal */} {selectedDoc && (

{selectedDoc.title || selectedDoc.dhivehi_title}

{selectedDoc.citation || 'N/A'}
{selectedDoc.jurisdiction}
{new Date(selectedDoc.date).toLocaleDateString()}
{selectedDoc.type === 'case_law' ? 'CASE LAW' : selectedDoc.type === 'statute' ? 'STATUTE' : 'LEGAL ARTICLE'}
{/* Keywords */}

Keywords

{selectedDoc.keywords.map((keyword, index) => ( {keyword} ))}
{/* Judges (for Case Law only) */} {selectedDoc.type === 'case_law' && selectedDoc.judges && selectedDoc.judges.length > 0 && (

Judges

{selectedDoc.judges.map((judge, index) => ( {judge} ))}
)} {/* Case Law Specific Fields */} {selectedDoc.type === 'case_law' && ( <> {/* Introduction */}

Introduction

{selectedDoc.introduction.en || selectedDoc.introduction.dv || 'N/A'}
{/* Ratio Decidendi */}

Ratio Decidendi

{selectedDoc.ratio.en || selectedDoc.ratio.dv || 'N/A'}
{/* Abstracts */}

Abstracts

{selectedDoc.abstracts?.map((ab, index) => (
{ab.page || 'N/A'} {ab.paragraph || 'N/A'}
{ab.en || ab.dv || 'N/A'}
))}
)} {/* Statute Specific Fields */} {selectedDoc.type === 'statute' && (

Statute Text

{selectedDoc.body?.en || selectedDoc.body?.dv || 'N/A'}
)} {/* Legal Article Specific Fields */} {selectedDoc.type === 'legal_article' && (

Article Content

{selectedDoc.body?.en || selectedDoc.body?.dv || 'N/A'}
)}
)} {/* Footer */}
); }